/**
 * ...
 * @author Rob Dravenstott
 * 
 * Primary application code to load images and create main weather display
 * 
 */

class WeatherRadar 
{
	private static var _app_root:MovieClip;
	private static var _first_mc:MovieClip;
	private static var _savenewsettingsFunc:Function;
	private static var _text_holder:MovieClip = null;
	private static var _radar_timer:Number = null;
	private static var _radar_timer_long:Number = null;
	private static var _radar_images:Array = new Array();
	private static var _next_radar_image:Number = 0;
	private static var _current_radar_mc:MovieClip;
	private static var _cp_btn:MovieClip = null;
	private static var _cp_ref:WeatherRadarConfig = null;
	
	// Can't find a way to determine original swf doc size, so just default
	private static var _original_doc_width:Number = 800;
	private static var _original_doc_height:Number = 600;
	
	// Set these thresholds for maximum photo dimensions to retrieve
	private static var _radar_width:Number = _original_doc_width;
	private static var _radar_height:Number = _original_doc_height;
	private static var _crop_ratio:String = "4:3";
	private static var _x_scale_factor:Number = 1;
	private static var _y_scale_factor:Number = 1;	

	private static var _radar_http_root:String = "http://radar.weather.gov/ridge/lite/";  //root directory for NWS images
	private static var _radar_zoom:Number = 150;  // % of Actual size (default = 150%)
	private static var _radar_id:String = "FTG";  // ftg=Denver area (default)
	private static var _radar_type:String = "N0R";  //NCR=Composite, N0R=Base, N0S=Storm, N0V=Velocity, N1P=One hour preciptiation, NTP=Total storm precipitation
	private static var _radar_max_images:Number = 5; // Max number of images for the sequence
	private static var _radar_image_display_pause:Number = 100; // How many ms to pause between images
	private static var _radar_end_image_display_pause:Number = 2000; // How many ms to pause on last image
	private static var _radar_long_restart_time:Number = 1000 * 60 * 3;  // Reload latest images every 3 minutes if no reload
	
	// calc variables for dragging images
	private static var _calc_x:Number = null;
	private static var _calc_y:Number = null;
	private static var _calc_width:Number = 0;
	private static var _calc_height:Number = 0;
	private static var _calc_mouse_start_x:Number = 0;
	private static var _calc_mouse_start_y:Number = 0;
	private static var _calc_dragging:Boolean = false;
	private static var _just_closed = false;
	
	//Listener objects
	private static var mouseListener:Object = null;
	private static var L:Object = null;
	
	// Initial function to get this started
	public static function run (app_root:MovieClip, settings:Object, saveNewSettings:Function)
	{
		WeatherRadar._app_root = app_root;
		WeatherRadar._savenewsettingsFunc = saveNewSettings;
		if (settings.radar_zoom != undefined)
			_radar_zoom = Number(settings.radar_zoom);
		if (settings.radar_type != undefined)
			_radar_type = settings.radar_type;
		if (settings.max_images != undefined)
			_radar_max_images = Number(settings.radar_max_images);
		if (settings.radar_id != undefined)
			_radar_id = String(settings.radar_id).toUpperCase();
		if (settings.calc_x != undefined)
			_calc_x = Number(settings.calc_x);
		if (settings.calc_y != undefined)
			_calc_y = Number(settings.calc_y);

		// Add listeners for opening config box and dragging images
		addListeners();
		
		// Get images loaded and start display
		restartShow(_calc_x,_calc_y);
	}
	
	private static function removeListeners():Void
	{
		Stage.removeListener(L);
		Mouse.removeListener(mouseListener);
	}
	
	private static function addListeners():Void
	{
		L = new Object();
		L.onResize = WeatherRadar.restartShow;
		Stage.addListener(L);
		
		mouseListener = new Object();
		mouseListener.onMouseDown = function() 
		{
			WeatherRadar._calc_mouse_start_x = WeatherRadar._app_root._xmouse;
			WeatherRadar._calc_mouse_start_y = WeatherRadar._app_root._ymouse;
			WeatherRadar._calc_dragging = false;
			WeatherRadar._just_closed = false;  // resets that we didn't just close dialogue box
			
			// Make sure image is loaded, and that the properties dialogue is not displayed
			if (   (WeatherRadar._current_radar_mc._width != 0)
			    && (WeatherRadar._current_radar_mc._height != 0)
				&& (WeatherRadar._cp_ref == null))
			{
				// -------- radar_width
				// ---------------- mc._width
				//         -------- calc_width  (mc._width max negative amount to shift image)
				// 
				WeatherRadar._calc_width = Math.round(WeatherRadar._radar_width - WeatherRadar._current_radar_mc._width);
				WeatherRadar._calc_height = Math.round(WeatherRadar._radar_height - WeatherRadar._current_radar_mc._height);
				
				// If there's room to move, let's stop the image sequence display timer and begin dragging
				if (   (WeatherRadar._calc_height < 0)
					|| (WeatherRadar._calc_width < 0))
				{			
					WeatherRadar.stopTimer();
					WeatherRadar._current_radar_mc.startDrag(false, WeatherRadar._calc_width, WeatherRadar._calc_height);
					WeatherRadar._calc_dragging = true;
				}
			}
			
			// If config not displayed, then image hasn't loaded yet so let's reset position to reload later
			else if (WeatherRadar._cp_ref == null)
			{
				WeatherRadar._calc_x = null;
				WeatherRadar._calc_y = null;
			}
		}
		
		// When we lift the mouse button up
		mouseListener.onMouseUp = function() 
		{
			// If we dragged, let's see how far we moved
			if (WeatherRadar._calc_dragging == true)
			{			
				WeatherRadar._current_radar_mc.stopDrag();
			}
				
			// Check properties not displayed if we truly moved or not
			if (   (WeatherRadar._cp_ref == null)
				&& (   (WeatherRadar._calc_mouse_start_x != WeatherRadar._app_root._xmouse)
					|| (WeatherRadar._calc_mouse_start_y != WeatherRadar._app_root._ymouse)))
			{
				//Reposition Image as appropriate and save position if loaded ok
				if (WeatherRadar.correctImagePosition() == true)
				{
					// Let's save the new position
					var settings:Object = new Object();
					settings.radar_zoom = WeatherRadar._radar_zoom;
					settings.radar_type = WeatherRadar._radar_type;
					settings.radar_max_images = WeatherRadar._radar_max_images;
					settings.radar_id = WeatherRadar._radar_id;
					settings.calc_x = WeatherRadar._calc_x;
					settings.calc_y = WeatherRadar._calc_y;
					WeatherRadar._savenewsettingsFunc(settings);
				}
			}
			
			// Had to add this clause to prevent race condition when dialogue box is being closed
			else if (WeatherRadar._just_closed == false)
			{
				// Let's open the settings dialogue if not open already
				if (WeatherRadar._cp_ref == null)
				{
					// We didn't move, so let's open settings
					WeatherRadar.openCPSettings();
				}
				
				// Dialogue is still open, let's see if click is outside and close
				else if (    (WeatherRadar._cp_ref != null)
						  && (   (WeatherRadar._app_root._xmouse > 320)
							  || (WeatherRadar._app_root._ymouse > 240)))
				{
					WeatherRadar.saveCPSettings(null, false)
				}
			}
			
			// start display images again
			if (WeatherRadar._calc_dragging == true)
			{
				WeatherRadar.startTimer();
			}
		}
		
		Mouse.addListener(mouseListener);
		
	}
	
	// Called after image is dragged or first loaded to validate new image position
	//   Returns true if we can actually correct
	private static function correctImagePosition():Boolean
	{
		// Make sure the current image loaded
		if (   (WeatherRadar._current_radar_mc._width != 0)
			&& (WeatherRadar._current_radar_mc._height != 0))
		{
			// -------- radar_width
			// ---------------- mc._width
			//         -------- calc_width  (mc._width max negative amount to shift image)
			// 
			// Need to re-calc here in case the image has reloaded
			WeatherRadar._calc_width = Math.round(WeatherRadar._radar_width - WeatherRadar._current_radar_mc._width);
			WeatherRadar._calc_height = Math.round(WeatherRadar._radar_height - WeatherRadar._current_radar_mc._height);
			
			// Check to make sure current image x position is between calc_width and 0 or reset it
			if (WeatherRadar._current_radar_mc._x > 0)
			{
				// If this is a positive number, image width is smaller than radar so center it
				if (WeatherRadar._calc_width > 0)
					WeatherRadar._current_radar_mc._x = Math.round((WeatherRadar._radar_width - WeatherRadar._current_radar_mc._width) / 2);
					
				// Otherwise, just set it back to 0
				else
					WeatherRadar._current_radar_mc._x = 0;
			}
			else if (WeatherRadar._current_radar_mc._x < WeatherRadar._calc_width)
				WeatherRadar._current_radar_mc._x = WeatherRadar._calc_width;
										
			// Check to make sure y position is between calc_height and 0 or reset it
			if (WeatherRadar._current_radar_mc._y > 0)
			{
				if (WeatherRadar._calc_height > 0)
					WeatherRadar._current_radar_mc._y = Math.round((WeatherRadar._radar_height - WeatherRadar._current_radar_mc._height) / 2);
				else
					WeatherRadar._current_radar_mc._y = 0;
			}
			else if (WeatherRadar._current_radar_mc._y < WeatherRadar._calc_height)
				WeatherRadar._current_radar_mc._y = WeatherRadar._calc_height;
			
			// Now determine what to adjust going forward
			WeatherRadar._calc_x = WeatherRadar._current_radar_mc._x;
			WeatherRadar._calc_y = WeatherRadar._current_radar_mc._y;
			
			return true;
		}
		
		// Image hasn't loaded yet, do nothing
		else
		{
		}
		
		return false;
	}
	
	// Called from Long restart timer
	private static function restartShowLong():Void
	{
		trace("Restarting long timer");
		restartShow(_calc_x, _calc_y);
	}
	
	// Function to load images and get timer going to display
	private static function restartShow(calc_x:Number, calc_y:Number):Void
	{
		stopTimer();
		stopLongTimer();
		
		// Re-Scale the buttons appropriately
		_x_scale_factor = Stage.width / _original_doc_width;
		_y_scale_factor = Stage.height / _original_doc_height;
		
		// Set radar max to stage 
		_radar_width = Stage.width;
		_radar_height = Stage.height;
		
		// Reset any repositioning that has occurred
		_calc_x = null;
		_calc_y = null;
		if (calc_x != undefined)
			_calc_x = calc_x;
		if (calc_y != undefined)
			_calc_y = calc_y;
		
		// Delete previous mc if not first time
		if (_first_mc != undefined)
		{
			_first_mc.removeMovieClip();
		}
		
		//Load initial image format: http://radar.weather.gov/ridge/lite/N0R/ftg_0.png
		_first_mc = _app_root.createEmptyMovieClip("first_mc", 5);
		loadIt(_first_mc, _radar_http_root + _radar_type + "/" + _radar_id.toUpperCase() + "_0.png", WeatherRadar.doneLoadingImage);
		_current_radar_mc = _first_mc;
		
		_next_radar_image = 0;
	}

	// After we complete loading the first image, then start to load others (issues on Chumby with Multi-threading loads)
	public static function doneLoadingImage(target_mc:MovieClip, errorDesc:String)
	{
		WeatherRadar.showIt(target_mc, errorDesc);

		// Need to add this as a double-check of position for first image load in case screen resolution has changed
		WeatherRadar.correctImagePosition()		
		
		// Builds an array of all the radar images
		createImageArray(_radar_images, _radar_max_images);
				
		// Loads up image array and then starts displaying them
		loadArrayImages(_radar_images, false, WeatherRadar.startTimer);
		
		startLongTimer();
	}
	
	
	private static function createImageArray(startArray:Array, maxImageNum:Number):Void
	{
		// delete any prior objects if here
		while (startArray.length > 0)
		{
			var po:Object = startArray.pop();

			if (po.mc != undefined)
				po.mc.removeMovieClip();

			delete po.mc;
			delete po.image;
		}

		// Add all our image names and a movieclip for each to the array
		for (var i:Number = maxImageNum; i > -1 ; i-- )
		{
			var po:Object = new Object();

			// Image url FORMAT:  http://radar.weather.gov/ridge/lite/N0R/ftg_0.png
			po.image = _radar_http_root + _radar_type + "/" + _radar_id.toUpperCase() + "_" + i.toString() + ".png";
			po.mc = _app_root.createEmptyMovieClip( "radarimg_" + i.toString(), 10 + i);
			po.loaded = false;
			startArray.push(po);
		}
	}
	
	public static function openCPSettings ():Void 
	{
		
		// Check if config box already open
		if (_cp_ref != null)
		{
			// Check if we clicked outside the dialog
			if (   (WeatherRadar._app_root._xmouse > 320)
			    || (WeatherRadar._app_root._ymouse > 240))
			{
				// Then close without saving
				_cp_ref.closeSettings();
				delete _cp_ref;
				_cp_ref = null;
			}

		}

		// Let's make sure the screen is big enough and we didn't just do a drag
		else if (   (Stage.width >= 320)
				 && (Stage.height >= 240)
				 && (WeatherRadar._calc_mouse_start_x == WeatherRadar._app_root._xmouse)
				 && (WeatherRadar._calc_mouse_start_y == WeatherRadar._app_root._ymouse))
		{
			// Open up the configuration settings dialog
			_cp_ref = new WeatherRadarConfig( _app_root, { radar_zoom:String(_radar_zoom), radar_id:_radar_id.toUpperCase(), radar_type:_radar_type, radar_max_images:_radar_max_images }, WeatherRadar.saveCPSettings);
		}
	}
	
	//called from the control panel dialog
	public static function saveCPSettings (settings:Object, saveIt:Boolean):Void 
	{
		if (   (saveIt == undefined)
			|| (saveIt == true))
		{
			var changed_pos:Boolean = false;
			
			// Check if either zoom or the radar location changed, then we can't re-use position
			if (   (_radar_zoom != Number(settings.radar_zoom))
				|| (_radar_id != String(settings.radar_id).toUpperCase()))
			{
				changed_pos = true;
			}
			
			// set items from callback
			_radar_zoom = Number(settings.radar_zoom);
			_radar_id = String(settings.radar_id).toUpperCase();
			_radar_type = settings.radar_type;
			_radar_max_images = Number(settings.radar_max_images);
						
			//save settings to db
			_savenewsettingsFunc(settings);
			
			// Let's start the process over again
			if (changed_pos == true)
				restartShow();
			else
				restartShow(_calc_x, _calc_y);
		}

		// Close dialog box
		if (_cp_ref != null)
		{
			_cp_ref.closeSettings();
			delete _cp_ref;
			_cp_ref = null;
			_just_closed = true;  // added this to prevent race condition
		}

	}
	
	// Adds random num string to url with number of ms - avoids unintended caching
	public static function addURLRandomizer():String
	{
		var curr_date:Date = new Date();
		var curr_time:Number = curr_date.getTime();
		
		return "?cachebuster=" + curr_time;
	}
	
	// Primary function to load individual images
	public static function loadIt (load_clip:MovieClip, file_path:String, callbackFunc:Function):Void
	{
		var loadListener:Object = new Object();
		
		// Called after image is loaded and it's ready to play
		// (this is when properties such as _width are set)
		loadListener.onLoadInit = function (target_mc:MovieClip) 
		{
			// We're only going to show images bigger than 10K since NWS error messages show up as < 10K files
			if (   (callbackFunc != undefined)
			    && (callbackFunc != null))
			{
				if (target_mc.getBytesLoaded() > 10000)
				{
					callbackFunc(target_mc);
				}
				else
				{
					target_mc._alpha = 0;
					callbackFunc(target_mc, "small file loaded");
				}
			}
		}

		loadListener.onLoadError = function (target_mc:MovieClip, errorCode:String, httpStatus:Number) 
		{
			WeatherRadar.showTextMessage("Load failed: " + errorCode + " http_error:" + httpStatus + " path:" + file_path, 8);
			trace("Load failed: " + errorCode + " http_error:" + httpStatus + " path:" + file_path);
			callbackFunc(target_mc, errorCode);
		}

		// Create new movie clip loader, add listener functions from above
		var mcLoader:MovieClipLoader = new MovieClipLoader();
		mcLoader.addListener(loadListener);

		load_clip._alpha = 0;
		// Load the actual photo into the clip
		mcLoader.loadClip(file_path+WeatherRadar.addURLRandomizer(), load_clip);
	}
	
	// Preload an array of images for display later
	public static function loadArrayImages(preloadArray:Array, showImages:Boolean, callBack:Function):Void
	{
		var loadListener:Object = new Object();
		var mcLoader:MovieClipLoader = new MovieClipLoader();
		var nextItem:Number = 0;

		loadListener.onLoadInit = function (target_mc:MovieClip)
		{
			// Don't display loaded is unavailable page
			if (target_mc.getBytesTotal() > 10000)
			{
				preloadArray[nextItem].loaded = true;
				trace("Loaded "+target_mc._url+" bytes = " + target_mc.getBytesTotal());
			}
			else
				trace("Load failed bytes = " + target_mc.getBytesTotal());
			
			if (!showImages)
				target_mc._alpha = 0;
			else
			{
				WeatherRadar.showIt(target_mc);
			}

			nextItem++;
			
			// Check to see if there's more to do
			if (nextItem < preloadArray.length) 
			{ 
				mcLoader.loadClip(preloadArray[nextItem].image+WeatherRadar.addURLRandomizer(), preloadArray[nextItem].mc);
			} 
			else 
			{
				// We're done so let's call the callBack if appropriate
				if (callBack != undefined)
					callBack();
			}
		}
		
		loadListener.onLoadError = function (target_mc:MovieClip, errorCode:String, httpStatus:Number) 
		{
			trace("Load failed: " + errorCode + " http_error:" + httpStatus + " path:" + preloadArray[nextItem].image);
			WeatherRadar.showTextMessage("Load failed: " + errorCode + " http_error:" + httpStatus + " path:" + preloadArray[nextItem].image, 8);
			
			// We're done so let's call the callBack if appropriate
			if (callBack != undefined)
				callBack();
		}

		mcLoader.addListener(loadListener);
		mcLoader.loadClip(preloadArray[nextItem].image+WeatherRadar.addURLRandomizer(), preloadArray[nextItem].mc);
	}
	
	
	// Hide previous radar image, show next
	public static function showNextRadar()
	{
		// Check to see if timer was already stopped
		if (checkTimer() == false)
			return;

		stopTimer();
		
		if (_radar_images[_next_radar_image].loaded == true)
		{
			// Set the current radar back to hidden
			_current_radar_mc._alpha = 0;
		
			showIt(_radar_images[_next_radar_image].mc);
			
			_current_radar_mc = _radar_images[_next_radar_image].mc;
		}
		
		_next_radar_image++;
		
		if (_next_radar_image >= _radar_images.length)
		{
			_next_radar_image = 0;
			
			// On the last image, let's pause for a couple seconds
			startTimer(_radar_end_image_display_pause);
		}
		else
			startTimer(_radar_image_display_pause);

	}
	
	// return false if timer not set
	private static function checkTimer():Boolean
	{
		if (_radar_timer == null)
			return false;
		else
			return true;
	}
	
	private static function stopTimer():Void
	{
		if (_radar_timer != null)
			clearInterval(_radar_timer);
			
		_radar_timer = null;
	}
	
	private static function stopLongTimer():Void
	{
		if (_radar_timer_long != null)
			clearInterval(_radar_timer_long);
			
		_radar_timer_long = null;
	}
	
	// Timer for radar sequences
	private static function startTimer(timerLength:Number):Void
	{
		stopTimer();

		if (timerLength == undefined)		
			_radar_timer =  setInterval(showNextRadar, 100);
		else
			_radar_timer =  setInterval(showNextRadar, timerLength);
			
	}
	
	// Timer to reload all images periodically
	private static function startLongTimer():Void
	{
		stopLongTimer();
		
		_radar_timer_long = setInterval(restartShowLong, _radar_long_restart_time);
	}
	
	public static function showIt (target_mc:MovieClip, errorDesc:String):Void
	{
		if (errorDesc == undefined)
		{
			if (   (target_mc != undefined)
			    && (target_mc != null))
			{
				// Center images to screen width and current zoom level
				target_mc._alpha = 100;
				target_mc._xscale = _radar_zoom;
				target_mc._yscale = _radar_zoom;
				
				if (_calc_x == null)
					_calc_x = Math.round((_radar_width - target_mc._width) / 2);
				target_mc._x = _calc_x;

				if (_calc_y == null)
					_calc_y = Math.round((_radar_height - target_mc._height) / 2);
				target_mc._y = _calc_y;
			}
		}
	}

	static function showTextMessage (myText:String, myTextSize:Number):Void 
	{
		// Delete any other previous messages if there
		deleteTextMessage();
		
		WeatherRadar._text_holder = _root.createEmptyMovieClip("text_holder", _app_root.getNextHighestDepth());
		WeatherRadar._text_holder.createTextField("t", WeatherRadar._text_holder.getNextHighestDepth(), 0, 20, Stage.width, Math.round(35 * WeatherRadar._y_scale_factor));
		
		var tf:TextFormat = new TextFormat();
		tf.size = myTextSize*WeatherRadar._x_scale_factor;
		tf.align = "center";
		tf.color = 0x000000; //black text
		tf.bold = true;
		tf.font = "_sans"; //also _serif and _typewriter available for chumby

		WeatherRadar._text_holder.t.setNewTextFormat(tf);
		WeatherRadar._text_holder.t.text = myText;											
		WeatherRadar._text_holder.t.wordWrap = true;
		delete tf;
	}
	
	private static function deleteTextMessage ():Void
	{
		if (WeatherRadar._text_holder != null)
		{
			WeatherRadar._text_holder.t.removeTextField();
			WeatherRadar._text_holder.removeMovieClip();
			WeatherRadar._text_holder = null;
		}
	}
	
}